package com.ecarx.camera.worker.car.aosp;

import android.car.Car;
import android.car.drivingstate.CarUxRestrictions;
import android.car.drivingstate.CarUxRestrictionsManager;
import android.car.hardware.power.CarPowerManager;
import android.car.hardware.property.CarPropertyManager;
import android.car.media.CarAudioManager;
import android.content.ComponentName;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.SystemClock;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;

import com.ecarx.camera.utils.AppExecutors;
import com.ecarx.camera.utils.BuildVersionUtils;
import com.ecarx.camera.utils.LogUtils;
import com.ecarx.camera.utils.Utils;
import com.ecarx.camera.utils.rx.RxUtils;
import com.ecarx.camera.worker.camera.dvr.DvrManager;
import com.ecarx.camera.worker.car.EmptyProxyImpl;
import com.ecarx.camera.worker.car.ICarFunctionProxy;
import com.ecarx.camera.worker.car.ICarSensorProxy;
import com.ecarx.camera.worker.car.IDeviceInfoProxy;
import com.ecarx.camera.worker.car.adaptapi.AdaptWrapperHelper;
import com.ecarx.camera.worker.car.sensor.SensorManager;

import java.util.List;

/**
 * Aosp中Car的管理类
 */
public class AospCarManager {

    private static final String TAG = "AospCarManager";
    /**
     * FIXME: 是否需要在CarPropertyManager获取成功时检测所有dvr的functionId的可用性（正式发布Dvr功能时，需要赋值为false）
     */
    private static final boolean NEED_CHECK_ALL_DVR_FUNCTION_IDS = true;

    private static final long WAIT_TIMEOUT_MS_FOR_CREATE_CAR = 1000;

    private STRStateListener strStateListener = null;
    private CarPowerManager mCarPowerManager;

    private Car mCar;
    private CarPropertyManager mCarPropertyManager;
    private CarAudioManager mCarAudioManager;
    private CarUxRestrictionsManager mCarUxRestrictionsManager;

    private final ICarFunctionProxy mCarFunctionProxy;
    private final ICarSensorProxy mCarSensorProxy;
    private final IDeviceInfoProxy mDeviceInfoProxy;

    private final EmptyProxyImpl mEmptyProxyImpl = new EmptyProxyImpl();

    private final MutableLiveData<Boolean> isRequiresDistractionOptimization = new DistinctLiveData<>(false);

    private RxUtils.ObservableAction<Boolean> mInitAction = new RxUtils.ObservableAction<Boolean>() {
        @Override
        public Boolean run(Object... parameters) {
            long start = SystemClock.elapsedRealtime();
            if (BuildVersionUtils.isAndroid11OrAbove()) {
                try {
                    Car.createCar(Utils.getApp(), AppExecutors.get().mainHandler(), WAIT_TIMEOUT_MS_FOR_CREATE_CAR, new Car.CarServiceLifecycleListener() {
                        @Override
                        public void onLifecycleChanged(Car car, boolean ready) {
                            mCar = car;
                            if (ready) {
                                onCarReady();
                                initCarPowerManager("onLifecycleChanged");
                            }
                        }
                    });
                } catch (Throwable e) {
                    LogUtils.e(TAG, "Car createCar for aboveAndroid11, e:" + e);
                }
            } else {
                try {
                    mCar = Car.createCar(Utils.getApp(), new ServiceConnection() {
                        @Override
                        public void onServiceConnected(ComponentName name, IBinder service) {
                            onCarReady();
                            initCarPowerManager("onServiceConnected");
                        }

                        @Override
                        public void onServiceDisconnected(ComponentName name) {
                            LogUtils.d(TAG, "onServiceDisconnected, reconnect, lastCar:" + mCar);
                            if (mCar != null) {
                                mCar.connect();
                            }
                        }
                    });
                } catch (Throwable e) {
                    LogUtils.e(TAG, "Car createCar e:" + e);
                }

                if (mCar != null) {
                    mCar.connect();
                }

            }
            LogUtils.d(TAG, "createCar cost:" + (SystemClock.elapsedRealtime() - start));
            return true;
        }
    };

    private void onCarReady() {
        if (mCar != null) {
            mCarPropertyManager = (CarPropertyManager) mCar.getCarManager(Car.PROPERTY_SERVICE);
            mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
            mCarUxRestrictionsManager = (CarUxRestrictionsManager) mCar.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
            mCarUxRestrictionsManager.registerListener(sOnUxRestrictionsChangedListener);
            LogUtils.d(TAG, "onCarReady, car:" + mCar + ", carPropertyManager:" + mCarPropertyManager);
            DvrManager.get().init();
            SensorManager.get().init();

            if (NEED_CHECK_ALL_DVR_FUNCTION_IDS) {
                checkDvrFunctionIdsActive();
            }
        }
    }

    private static class Holder {
        private static final AospCarManager INSTANCE = new AospCarManager();
    }

    public static AospCarManager get() {
        return Holder.INSTANCE;
    }

    private AospCarManager() {
        mCarFunctionProxy = new CarFunctionProxyAospImpl();
        mCarSensorProxy = new CarSensorProxyAospImpl();
        mDeviceInfoProxy = new DeviceInfoProxyAospImpl();
    }

    /**
     * 初始化（Application初始化时调用）
     */
    public void init() {
        if (!isConnected()) {
            RxUtils.doAction(mInitAction, null);
        }
    }

    /**
     * 判断CarService是否连接成功
     * @return
     */
    public boolean isConnected() {
        return mCar != null && mCar.isConnected();
    }

    /**
     * 判断CarPropertyManger是否初始化成功
     * @return
     */
    private boolean isCarPropertyManagerReady() {
        return mCarPropertyManager != null;
    }

    /**
     * 获取CarPropertyManager
     * @return
     */
    CarPropertyManager getCarPropertyManager() {
        return mCarPropertyManager;
    }

    /**
     * 获取CarAudioManager
     * @return
     */
    CarAudioManager getCarAudioManager() {
        return mCarAudioManager;
    }

    public ICarFunctionProxy getCarFunctionProxy() {
        if (isCarPropertyManagerReady()) {
            return mCarFunctionProxy;
        }
        return mEmptyProxyImpl;
    }

    public ICarSensorProxy getCarSensorProxy() {
        if (isCarPropertyManagerReady()) {
            return mCarSensorProxy;
        }
        return mEmptyProxyImpl;
    }

    public IDeviceInfoProxy getDeviceInfoProxy() {
        if (isCarPropertyManagerReady()) {
            return mDeviceInfoProxy;
        }
        return mEmptyProxyImpl;
    }

    /**
     * 检测所有dvr相关的functionIds是否可用
     */
    private void checkDvrFunctionIdsActive() {
        AppExecutors.get().executeOnIO(new Runnable() {
            @Override
            public void run() {
                List<Integer> functionIds = AdaptWrapperHelper.getAllFunctionIds();
                int size = functionIds == null ? 0 : functionIds.size();
                for (int i = 0; i < size; i++) {
                    int functionId = functionIds.get(i);
                    if (!AospCarManager.get().getCarFunctionProxy().isFunctionSupported(functionId)) {
                        LogUtils.d(TAG, "checkDvrFunctionIdsActive-> functionId:" + functionId + ", propId:0x" + Integer.toHexString(AdaptWrapperHelper.functionIdToPropertyId(functionId)) + ", not supported");
                    } else if (!AospCarManager.get().getCarFunctionProxy().isFunctionActive(functionId)) {
                        LogUtils.d(TAG, "checkDvrFunctionIdsActive-> functionId:" + functionId + ", propId:0x" + Integer.toHexString(AdaptWrapperHelper.functionIdToPropertyId(functionId)) + ", not available");
                    } else {
                        LogUtils.d(TAG, "checkDvrFunctionIdsActive-> functionId:" + functionId + ", propId:0x" + Integer.toHexString(AdaptWrapperHelper.functionIdToPropertyId(functionId)) + ", supported and available");
                    }
                }
            }
        });
    }

    private final android.car.drivingstate.CarUxRestrictionsManager.OnUxRestrictionsChangedListener sOnUxRestrictionsChangedListener = new android.car.drivingstate.CarUxRestrictionsManager.OnUxRestrictionsChangedListener(){

        @Override
        public void onUxRestrictionsChanged(CarUxRestrictions carUxRestrictions) {
            LogUtils.d(TAG, "onUxRestrictionsChanged isRequiresDistractionOptimization = " + carUxRestrictions.isRequiresDistractionOptimization());
            isRequiresDistractionOptimization.postValue(carUxRestrictions.isRequiresDistractionOptimization());
        }
    };

    public LiveData<Boolean> isRequiresDistractionOptimization() {
        synchronized (CarUxRestrictionsManager.class) {
            if (mCar == null) {
                return isRequiresDistractionOptimization;
            }
            if (!mCar.isConnected()) {
                LogUtils.d(TAG, "error isConnected = false");
            }
            if(mCarUxRestrictionsManager == null){
                LogUtils.d(TAG, "init registerListener");
                mCarUxRestrictionsManager = (CarUxRestrictionsManager) mCar.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE);
                mCarUxRestrictionsManager.registerListener(sOnUxRestrictionsChangedListener);
            } else {
                LogUtils.d(TAG, "already registerListener");
            }
        }
        return isRequiresDistractionOptimization;
    }

    /**
     * STR ---------------start-------------------
     * */
    public void setSTRStateListener(STRStateListener strStateListener) {
        this.strStateListener = strStateListener;
    }

    public interface STRStateListener {
        void onStateChanged(int state);
    }

    /*****  STR *****/
    private final CarPowerManager.CarPowerStateListener mPowerListener = state -> {
        LogUtils.d(TAG, "bindSTR onStateChanged: state =" + state);
        if (strStateListener != null) {
            strStateListener.onStateChanged(state);
        }
    };

    private void initCarPowerManager(String tag) {
        LogUtils.d(TAG, "bindSTR initCarPowerManager: " + tag + ": connected to car service");
        try {
            mCarPowerManager = (CarPowerManager) mCar.getCarManager(Car.POWER_SERVICE);
            mCarPowerManager.setListener(mPowerListener);
        } catch (Exception e) {
            LogUtils.e(TAG, "bindSTR initCarPowerManager Error : " + e);
        }
    }

    public void onDestroy() {
        // Destroy
        if (mCarPowerManager != null) {
            LogUtils.d(TAG, "bindSTR onDestroy clearListener");
            mCarPowerManager.clearListener();
        }
    }
    /**
     * STR ---------------end-------------------
     */
}
